<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

<!--<html onclick="keepFocusInTextbox(event)">-->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<LINK REL="SHORTCUT ICON" HREF="/img/insect_page_icon.gif">
<script src="/soap/ajax/10.0/connection.js" type="text/javascript"></script>
<script src="/soap/ajax/10.0/apex.js" type="text/javascript"></script>
<script src="/js/functions.js"></script>
<title>Shell</title>

<script type="text/javascript">

var histList = [""],
        histPos = 0,
        _scope = {},
        _win, // a top-level context
        question,
        _in,
        _out,
        tooManyMatches = null,
        lastError = null;

function refocus()
{
    _in.blur();
    // Needed for Mozilla to scroll correctly.
    _in.focus();
}

function init()
{

    if (!sforce.connection.sessionId) {
        var sid = getCookie("sid");
        if (sid) {
            sforce.connection.sessionId = sid;
        } else {
            window.location = "/?startURL=" + window.location;
            return;
        }
    }

    _in = document.getElementById("input");
    _out = document.getElementById("output");

    _win = window;

    if (opener && !opener.closed)
    {
        //println("Using bookmarklet version of shell: commands will run in opener's context.", "message");
        _win = opener;
    }

    initTarget();

    recalculateInputHeight();
    enableShell();
    refocus();
}

function initTarget()
{
    _win.Shell = window;
    _win.print = shellCommands.print;
}


// Unless the user is selected something, refocus the textbox.
// (requested by caillon, brendan, asa)
function keepFocusInTextbox(e)
{
    var g = e.srcElement ? e.srcElement : e.target;
    // IE vs. standard

    while (!g.tagName)
        g = g.parentNode;
    var t = g.tagName.toUpperCase();
    if (t == "A" || t == "INPUT")
        return;

    if (window.getSelection) {
        // Mozilla
        if (String(window.getSelection()))
            return;
    }
    else if (document.getSelection) {
        // Opera? Netscape 4?
        if (document.getSelection())
            return;
    }
    else {
        // IE
        if (document.selection.createRange().text)
            return;
    }

    refocus();
}

function execScript() {
    var lang = document.getElementById("lang").value;
    if ("JavaScript" == lang) {
        _win.location.href = "javascript:try{ Shell.printAnswer(eval('with(Shell._scope) with(Shell.shellCommands) {' + Shell.question + String.fromCharCode(10) + '}')); } catch(er) { Shell.printError(er); }; setTimeout(Shell.refocus, 0); void 0";
    } else if ("SOQL" == lang) {
        try {
            var result = sforce.connection.query(_in.value);
            println(result, "normalOutput");
        } catch(error) {
            printError(error);
        }
     } else if ("SOSL" == lang) {
        try {
            var result = sforce.connection.search(_in.value);
            println(result, "normalOutput");
        } catch(error) {
            printError(error);
        }
     } else if ("Apex" == lang) {
        try {
            var result = sforce.apex.executeAnonymous(_in.value);
            println(result, "normalOutput");
        } catch(error) {
            printError(error);
        }
    } else {
        printError("unknown lang " + lang);
    }

    setTimeout(function() {
        _in.value = "";
    }, 0);
    // can't preventDefault on input, so clear it later
}

function inputKeydown(e) {
    // Use onkeydown because IE doesn't support onkeypress for arrow keys

    //alert(e.keyCode + " ^ " + e.keycode);

    if (e.shiftKey && e.keyCode == 13) { // shift-enter
        // don't do anything; allow the shift-enter to insert a line break as normal
    } else if (e.keyCode == 13) { // enter
        // execute the input on enter
        if (document.getElementById("multiline").checked) {
            if (e.ctrlKey) {
                try { go(); } catch(er) { alert(er); };
            }
        } else {
            try { go(); } catch(er) { alert(er); };
        }
    } else if (e.keyCode == 38) { // up
        // go up in history if at top or ctrl-up
        if (e.ctrlKey || caretInFirstLine(_in))
            hist(true);
    } else if (e.keyCode == 40) { // down
        // go down in history if at end or ctrl-down
        if (e.ctrlKey || caretInLastLine(_in))
            hist(false);
    } else if (e.keyCode == 9) { // tab
        if ("JavaScript" == document.getElementById("lang").value) {
          tabcomplete();
        }
        setTimeout(function() {
            refocus();
        }, 0);
        e.returnValue = false;
        // refocus because tab was hit
    } else {
    }

    setTimeout(recalculateInputHeight, 0);

    //return true;
}
;

function caretInFirstLine(textbox)
{
    // IE doesn't support selectionStart/selectionEnd
    if (textbox.selectionStart == undefined)
        return true;

    var firstLineBreak = textbox.value.indexOf("\n");

    return ((firstLineBreak == -1) || (textbox.selectionStart <= firstLineBreak));
}

function caretInLastLine(textbox)
{
    // IE doesn't support selectionStart/selectionEnd
    if (textbox.selectionEnd == undefined)
        return true;

    var lastLineBreak = textbox.value.lastIndexOf("\n");

    return (textbox.selectionEnd > lastLineBreak);
}

function recalculateInputHeight()
{
    var rows = _in.value.split(/\n/).length
            + 1 // prevent scrollbar flickering in Mozilla
            + (window.opera ? 1 : 0);
    // leave room for scrollbar in Opera

    if (_in.rows != rows) // without this check, it is impossible to select text in Opera 7.60 or Opera 8.0.
        _in.rows = rows;
}

//----------- table display

function convert(s, sb, isFirst) {
    if (s == null) {
        sb.append("null");
        return;
    }

    var tof = typeof s;

    if (tof == "string" || tof == "number" || tof == "function") {
        sb.append(s);
        return;
    } else if (tof == "object") {
        if (s.push && s.length > 0 && (typeof s[0] === "object")) {
            sb.append(makeExpandLink(isFirst) + "<table>");
            var heading = findHeading(s);
            sb.append("<tr>");
            for (var head in heading) {
                sb.append("<td><b>" + head + "</b></td>");
            }
            sb.append("</tr>");
            for (var i=0; i<s.length; i++) {
                sb.append("<tr>");
                for (var head in heading) {
                    sb.append("<td>");
                    convert(s[i][head], sb, false);
                    sb.append("</td>");
                }
                sb.append("</tr>");
            }
            sb.append("</table></span>");
        } else {
            sb.append(makeExpandLink(isFirst) + "<table>" );
            var arrays = [];
            for (var a in s) {
                if (typeof s[a] == "function") {
                    //skip
                } else if (s[a] && s[a].push) {
                    arrays[a] = s[a];
                } else {
                    sb.append("<tr><td><b>" + a + "</b></td><td>");
                    convert(s[a], sb, false);
                    sb.append("</td></tr>");
                }
            }
            for (var a in arrays) {
                sb.append("<tr><td colspan='2'>");
                sb.append("<b>" + a + "</b>");
                convert(arrays[a], sb, false);
                sb.append("</td></tr>")
            }
            sb.append("</table></span>");
        }
    }
}

var _linkCount = 0;
function makeExpandLink(isFirst) {
    _linkCount++;
    var rand = new Date().getTime() + "-" + _linkCount;
    var display = isFirst ? "block" : "none";
    //return "<img src=\"/img/recurring_activity.gif\" onclick='javascript: displayElement(\""+ rand +"\"); void 0;'>" +
           //"<span style='display: " + display + "' id='" + rand + "'>";
    var img = isFirst ? "/js/dojo/0.4.1/src/widget/templates/images/treenode_expand_minus.gif" :
              "/js/dojo/0.4.1/src/widget/templates/images/treenode_expand_plus.gif";

    return "<img id=\"" + rand + "-img\" src=\""+ img + "\" " +
           "onclick='javascript: displayElement(\"" + rand + "\"); void 0;'>" +
           "<span style='display: " + display + "' id='" + rand + "'>";
}

function displayElement(id) {
    var doc = document.getElementById(id);
    if (doc.style.display == 'none') {
        doc.style.display = 'block';
        document.getElementById(id + "-img").src =
            "/js/dojo/0.4.1/src/widget/templates/images/treenode_expand_minus.gif";
    } else {
        doc.style.display = 'none';
        document.getElementById(id + "-img").src =
            "/js/dojo/0.4.1/src/widget/templates/images/treenode_expand_plus.gif";
    }
}

function findHeading(array) {
    var heading = [];
    for (var i=0; i<array.length; i++) {
        for (var a in array[i]) {
            if (typeof array[i][a] != "function") {
                heading[a] = true;
            }
        }
    }
    return heading;
}

//------------- end table display

function println(s, type)
{
    var sb = new sforce.StringBuffer();
    convert(s, sb, true);
    s = sb.toString();

    if ((s = String(s)))
    {
        var newdiv = document.createElement("div");
        newdiv.innerHTML = s;
        //newdiv.appendChild(document.createTextNode(s));
        newdiv.className = type;
        _out.appendChild(newdiv);
        return newdiv;
    }
}

function printWithRunin(h, s, type)
{
    var div = println(s, type);
    var head = document.createElement("strong");
    head.appendChild(document.createTextNode(h + ": "));
    div.insertBefore(head, div.firstChild);
}


var shellCommands =
{
    load : function load(url)
    {
        var s = _win.document.createElement("script");
        s.type = "text/javascript";
        s.src = url;
        _win.document.getElementsByTagName("head")[0].appendChild(s);
        println("Loading " + url + "...", "message");
    },

    clear : function clear()
    {
        var CHILDREN_TO_PRESERVE = 0;
        while (_out.childNodes[CHILDREN_TO_PRESERVE])
            _out.removeChild(_out.childNodes[CHILDREN_TO_PRESERVE]);

        var help = document.getElementById("help-text");
        help.setAttribute("style", "display: none");
    },

    print : function print(s) {
        println(s, "print");
    },

// the normal function, "print", shouldn't return a value
// (suggested by brendan; later noticed it was a problem when showing others)
    pr : function pr(s)
    {
        shellCommands.print(s);
        // need to specify shellCommands so it doesn't try window.print()!
        return s;
    },

    props : function props(e, onePerLine)
    {
        if (e === null) {
            println("props called with null argument", "error");
            return;
        }

        if (e === undefined) {
            println("props called with undefined argument", "error");
            return;
        }

        var ns = ["Methods", "Fields", "Unreachables"];
        var as = [[], [], []];
        // array of (empty) arrays of arrays!
        var p, j, i;
        // loop variables, several used multiple times

        var protoLevels = 0;

        for (p = e; p; p = p.__proto__)
        {
            for (i = 0; i < ns.length; ++i)
                as[i][protoLevels] = [];
            ++protoLevels;
        }

        for (var a in e)
        {
            // Shortcoming: doesn't check that VALUES are the same in object and prototype.

            var protoLevel = -1;
            try
            {
                for (p = e; p && (a in p); p = p.__proto__)
                    ++protoLevel;
            }
            catch(er) {
                protoLevel = 0;
            } // "in" operator throws when param to props() is a string

            var type = 1;
            try
            {
                if ((typeof e[a]) == "function")
                    type = 0;
            }
            catch (er) {
                type = 2;
            }

            as[type][protoLevel].push(a);
        }

        function times(s, n) {
            return n ? s + times(s, n - 1) : "";
        }

        for (j = 0; j < protoLevels; ++j)
            for (i = 0; i < ns.length; ++i)
                if (as[i][j].length)
                    printWithRunin(
                            ns[i] + times(" of prototype", j),
                            (onePerLine ? "\n\n" : "") + as[i][j].sort().join(onePerLine ? "\n" : ", ") + (onePerLine ? "\n\n" : ""),
                            "propList"
                            );
    },

    blink : function blink(node)
    {
        if (!node)                     throw("blink: argument is null or undefined.");
        if (node.nodeType == null)     throw("blink: argument must be a node.");
        if (node.nodeType == 3)        throw("blink: argument must not be a text node");
        if (node.documentElement)      throw("blink: argument must not be the document object");

        function setOutline(o) {
            return function() {
                if (node.style.outline != node.style.bogusProperty) {
                    // browser supports outline (Firefox 1.1 and newer, CSS3, Opera 8).
                    node.style.outline = o;
                }
                else if (node.style.MozOutline != node.style.bogusProperty) {
                    // browser supports MozOutline (Firefox 1.0.x and older)
                    node.style.MozOutline = o;
                }
                else {
                    // browser only supports border (IE). border is a fallback because it moves things around.
                    node.style.border = o;
                }
            }
        }

        function focusIt(a) {
            return function() {
                a.focus();
            }
        }

        if (node.ownerDocument) {
            var windowToFocusNow = (node.ownerDocument.defaultView || node.ownerDocument.parentWindow);
            // Moz vs. IE
            if (windowToFocusNow)
                setTimeout(focusIt(windowToFocusNow.top), 0);
        }

        for (var i = 1; i < 7; ++i)
            setTimeout(setOutline((i % 2)?'3px solid red':'none'), i * 100);

        setTimeout(focusIt(window), 800);
        setTimeout(focusIt(_in), 810);
    },

    scope : function scope(sc)
    {
        if (!sc) sc = {};
        _scope = sc;
        println("Scope is now " + sc + ".  If a variable is not found in this scope, window will also be searched.  New variables will still go on window.", "message");
    },

    mathHelp : function mathHelp()
    {
        printWithRunin("Math constants", "E, LN2, LN10, LOG2E, LOG10E, PI, SQRT1_2, SQRT2", "propList");
        printWithRunin("Math methods", "abs, acos, asin, atan, atan2, ceil, cos, exp, floor, log, max, min, pow, random, round, sin, sqrt, tan", "propList");
    },

    ans : undefined
};


function hist(up)
{
    // histList[0] = first command entered, [1] = second, etc.
    // type something, press up --> thing typed is now in "limbo"
    // (last item in histList) and should be reachable by pressing
    // down again.

    var L = histList.length;

    if (L == 1)
        return;

    if (up)
    {
        if (histPos == L - 1)
        {
            // Save this entry in case the user hits the down key.
            histList[histPos] = _in.value;
        }

        if (histPos > 0)
        {
            histPos--;
            // Use a timeout to prevent up from moving cursor within new text
            // Set to nothing first for the same reason
            setTimeout(
                    function() {
                        _in.value = '';
                        _in.value = histList[histPos];
                        var caretPos = _in.value.length;
                        if (_in.setSelectionRange)
                            _in.setSelectionRange(caretPos, caretPos);
                    },
                    0
                    );
        }
    }
    else // down
    {
        if (histPos < L - 1)
        {
            histPos++;
            _in.value = histList[histPos];
        }
        else if (histPos == L - 1)
        {
            // Already on the current entry: clear but save
            if (_in.value)
            {
                histList[histPos] = _in.value;
                ++histPos;
                _in.value = "";
            }
        }
    }
}

function tabcomplete()
{
    /*
    * Working backwards from s[from], find the spot
    * where this expression starts.  It will scan
    * until it hits a mismatched ( or a space,
    * but it skips over quoted strings.
    * If stopAtDot is true, stop at a '.'
    */
    function findbeginning(s, from, stopAtDot)
    {
        /*
        *  Complicated function.
        *
        *  Return true if s[i] == q BUT ONLY IF
        *  s[i-1] is not a backslash.
        */
        function equalButNotEscaped(s, i, q)
        {
            if (s.charAt(i) != q) // not equal go no further
                return false;

            if (i == 0) // beginning of string
                return true;

            if (s.charAt(i - 1) == '\\') // escaped?
                return false;

            return true;
        }

        var nparens = 0;
        var i;
        for (i = from; i >= 0; i--)
        {
            if (s.charAt(i) == ' ')
                break;

            if (stopAtDot && s.charAt(i) == '.')
                break;

            if (s.charAt(i) == ')')
                nparens++;
            else if (s.charAt(i) == '(')
                nparens--;

            if (nparens < 0)
                break;

            // skip quoted strings
            if (s.charAt(i) == '\'' || s.charAt(i) == '\"')
            {
                //dump("skipping quoted chars: ");
                var quot = s.charAt(i);
                i--;
                while (i >= 0 && !equalButNotEscaped(s, i, quot)) {
                    //dump(s.charAt(i));
                    i--;
                }
                //dump("\n");
            }
        }
        return i;
    }

    // XXX should be used more consistently (instead of using selectionStart/selectionEnd throughout code)
    // XXX doesn't work in IE, even though it contains IE-specific code
    function getcaretpos(inp)
    {
        if(inp.selectionEnd != null)
        return inp.selectionEnd;

        // IE specific code
        var range = document.selection.createRange();
        var isCollapsed = range.compareEndPoints("StartToEnd", range) == 0;
        if (!isCollapsed)
          range.collapse(false);
        var b = range.getBookmark();
        return b.charCodeAt(2) - 2;
/*
        if (inp.selectionEnd != null)
            return inp.selectionEnd;

        if (inp.createTextRange)
        {
            var docrange = _win.Shell.document.selection.createRange();
            var inprange = inp.createTextRange();
            if (inprange.setEndPoint)
            {
                inprange.setEndPoint('EndToStart', docrange);
                return inprange.text.length;
            }
        }

        return inp.value.length;
        // sucks, punt
        */
    }

    function setselectionto(inp, pos)
    {
        if (inp.selectionStart) {
            inp.selectionStart = inp.selectionEnd = pos;
        }
        else if (inp.createTextRange) {
            var docrange = _win.Shell.document.selection.createRange();
            var inprange = inp.createTextRange();
            inprange.move('character', pos);
            inprange.select();
        }
        else { // err...
            /*
          inp.select();
          if(_win.Shell.document.getSelection())
            _win.Shell.document.getSelection() = "";
            */
        }
    }
    // get position of cursor within the input box
    var caret = getcaretpos(_in);

    if (caret) {
        //dump("----\n");
        var dotpos, spacepos, complete, obj;
        //dump("caret pos: " + caret + "\n");
        // see if there's a dot before here
        dotpos = findbeginning(_in.value, caret - 1, true);
        //dump("dot pos: " + dotpos + "\n");
        if (dotpos == -1 || _in.value.charAt(dotpos) != '.') {
            dotpos = caret;
            //dump("changed dot pos: " + dotpos + "\n");
        }

        // look backwards for a non-variable-name character
        spacepos = findbeginning(_in.value, dotpos - 1, false);
        //dump("space pos: " + spacepos + "\n");
        // get the object we're trying to complete on
        if (spacepos == dotpos || spacepos + 1 == dotpos || dotpos == caret)
        {
            // try completing function args
            if (_in.value.charAt(dotpos) == '(' ||
                (_in.value.charAt(spacepos) == '(' && (spacepos + 1) == dotpos))
            {
                var fn,fname;
                var from = (_in.value.charAt(dotpos) == '(') ? dotpos : spacepos;
                spacepos = findbeginning(_in.value, from - 1, false);

                fname = _in.value.substr(spacepos + 1, from - (spacepos + 1));
                //dump("fname: " + fname + "\n");
                try {
                    with (_win.Shell._scope)
                        with (_win)
                            with (Shell.shellCommands)
                                fn = eval(fname);
                }
                catch(er) {
                    //dump('fn is not a valid object\n');
                    return;
                }
                if (fn == undefined) {
                    //dump('fn is undefined');
                    return;
                }
                if (fn instanceof Function)
                {
                    // Print function definition, including argument names, but not function body
                    if (!fn.toString().match(/function .+?\(\) +\{\n +\[native code\]\n\}/))
                        println(fn.toString().match(/function .+?\(.*?\)/), "tabcomplete");
                }

                return;
            }
            else
                obj = _win;
        }
        else
        {
            var objname = _in.value.substr(spacepos + 1, dotpos - (spacepos + 1));
            //dump("objname: |" + objname + "|\n");
            try {
                with (_win.Shell._scope)
                    with (_win)
                        obj = eval(objname);
            }
            catch(er) {
                printError(er);
                return;
            }
            if (obj == undefined) {
                // sometimes this is tabcomplete's fault, so don't print it :(
                // e.g. completing from "print(document.getElements"
                // println("Can't complete from null or undefined expression " + objname, "error");
                return;
            }
        }
        //dump("obj: " + obj + "\n");
        // get the thing we're trying to complete
        if (dotpos == caret)
        {
            if (spacepos + 1 == dotpos || spacepos == dotpos)
            {
                // nothing to complete
                //dump("nothing to complete\n");
                return;
            }

            complete = _in.value.substr(spacepos + 1, dotpos - (spacepos + 1));
        }
        else {
            complete = _in.value.substr(dotpos + 1, caret - (dotpos + 1));
        }
        //dump("complete: " + complete + "\n");
        // ok, now look at all the props/methods of this obj
        // and find ones starting with 'complete'
        var matches = [];
        var bestmatch = null;
        for (var a in obj)
        {
            //a = a.toString();
            //XXX: making it lowercase could help some cases,
            // but screws up my general logic.
            if (a.substr(0, complete.length) == complete) {
                matches.push(a);
                ////dump("match: " + a + "\n");
                // if no best match, this is the best match
                if (bestmatch == null)
                {
                    bestmatch = a;
                }
                else {
                    // the best match is the longest common string
                    function min(a, b) {
                        return ((a < b)?a:b);
                    }
                    var i;
                    for (i = 0; i < min(bestmatch.length, a.length); i++)
                    {
                        if (bestmatch.charAt(i) != a.charAt(i))
                            break;
                    }
                    bestmatch = bestmatch.substr(0, i);
                    ////dump("bestmatch len: " + i + "\n");
                }
                ////dump("bestmatch: " + bestmatch + "\n");
            }
        }
        bestmatch = (bestmatch || "");
        ////dump("matches: " + matches + "\n");
        var objAndComplete = (objname || obj) + "." + bestmatch;
        //dump("matches.length: " + matches.length + ", tooManyMatches: " + tooManyMatches + ", objAndComplete: " + objAndComplete + "\n");
        if (matches.length > 1 && (tooManyMatches == objAndComplete || matches.length <= 10)) {

            printWithRunin("Matches: ", matches.join(', '), "tabcomplete");
            tooManyMatches = null;
        }
        else if (matches.length > 10)
        {
            println(matches.length + " matches.  Press tab again to see them all", "tabcomplete");
            tooManyMatches = objAndComplete;
        }
        else {
            tooManyMatches = null;
        }
        if (bestmatch != "")
        {
            var sstart;
            if (dotpos == caret) {
                sstart = spacepos + 1;
            }
            else {
                sstart = dotpos + 1;
            }
            _in.value = _in.value.substr(0, sstart)
                    + bestmatch
                    + _in.value.substr(caret);
            setselectionto(_in, caret + (bestmatch.length - complete.length));
        }
    }
}

function printQuestion(q)
{
    println(q, "input");
}

function printAnswer(a)
{
    if (a !== undefined) {
        println(a, "normalOutput");
        shellCommands.ans = a;
    }
}

function printError(er)
{
    var lineNumberString;

    lastError = er;
    // for debugging the shell
    if (er.name)
    {
        // lineNumberString should not be "", to avoid a very wacky bug in IE 6.
        lineNumberString = (er.lineNumber != undefined) ? (" on line " + er.lineNumber + ": ") : ": ";
        println(er.name + lineNumberString + er.message, "error");
        // Because IE doesn't have error.toString.
    }
    else
        println(er, "error");
    // Because security errors in Moz /only/ have toString.
}

function go(s)
{
    _in.value = question = s ? s : _in.value;

    if (question == "")
        return;

    histList[histList.length - 1] = question;
    histList[histList.length] = "";
    histPos = histList.length - 1;

    // Unfortunately, this has to happen *before* the JavaScript is run, so that
    // print() output will go in the right place.
    //_in.value = '';
    recalculateInputHeight();
    printQuestion(question);

    if (_win.closed) {
        printError("Target window has been closed.");
        return;
    }

    try {
        ("Shell" in _win)
    }
    catch(er) {
        printError("The JavaScript Shell cannot access variables in the target window.  The most likely reason is that the target window now has a different page loaded and that page has a different hostname than the original page.");
        return;
    }

    if (!("Shell" in _win))
        initTarget(); // silent

    execScript();
    // Evaluate Shell.question using _win's eval (this is why eval isn't in the |with|, IIRC).
    //_win.location.href = "javascript:try{ Shell.printAnswer(eval('with(Shell._scope) with(Shell.shellCommands) {' + Shell.question + String.fromCharCode(10) + '}')); } catch(er) { Shell.printError(er); }; setTimeout(Shell.refocus, 0); void 0";
}

function enableShell() {
    var input = document.getElementById("input-div");
    input.innerHTML = "<textarea style='font-family: Courier New, Courier, monospace' id='input' class='input' " +
                      "wrap='off'  onkeydown='inputKeydown(event)' rows='1'></textarea>";
    _in = document.getElementById("input");

    //var help = document.getElementById("help-text");
    //help.setAttribute("style", "visibility: show");
    refocus();
}

</script>

<!-- for http://ted.mielczarek.org/code/mozilla/extensiondev/ -->
<script type="text/javascript" src="chrome://extensiondev/content/rdfhistory.js"></script>
<script type="text/javascript" src="chrome://extensiondev/content/chromeShellExtras.js"></script>

<style type="text/css">
body {
    background: white;
    color: black;
}

        img {
            border: 0;
        }

#output {
    white-space: pre;
    white-space: -moz-pre-wrap;
}

/* Preserve line breaks, but wrap too if browser supports it */
h3 {
    margin-top: 0;
    margin-bottom: 0em;
}

h3 + div {
    margin: 0;
}

form {
    margin: 0;
    padding: 0;
}

#input {
    width: 100%;
    /*border: none;*/
    padding: 0;
    overflow: auto;
}

.input {
    color: blue;
    background: white;
    font: inherit;
    font-weight: bold;
    margin-top: .5em; /* background: #E6E6FF; */
}

.normalOutput {
    color: black;
    background: white;
}

.print {
    color: brown;
    background: white;
}

.error {
    color: red;
    background: white;
}

.propList {
    color: green;
    background: white;
}

.message {
    color: green;
    background: white;
}

.tabcomplete {
    color: purple;
    background: white;
}

/*
        body {
            font-family: Arial, Tahoma, Verdana;
            font-size: 12px;
        }
        */

        TABLE {
            border-collapse: collapse;
            /*border: 1px dotted red;*/
            border: 1px solid black;
            font-size: 12px;
        }

        *#col1 {
            border: 3px solid black;
        }

        TD.dotted-black {
            border: 0px dotted black;
            padding: 10px;
            text-align: center;
        }

        TD {
            border: 1px dotted green;
            padding: .25em;
        }

        TD.solid-blue {
            border: 5px dashed blue;
        }

        TD.solid-green {
            border: 5px solid green;
        }
</style>

</head>

<body onload="init()">

            <table width="100%">
                <tr>
                    <td class="dotted-black" width=30>
                        <a href="javascript:displayElement('help-text'); void 0;">
                            <img src="/img/help/helpTips_icon.gif" width="24" height="24" alt="Open"></img></a><br>Help
                    </td>
                    <td class="dotted-black" width=30>
                        <a href="javascript:document.getElementById('lang').value = 'JavaScript'; go('clear()'); void 0;">
                            <img src="/img/classic/icon/circle_page_icon.gif" width="24" height="24" alt="Open"></img></a><br>Clear
                    </td>
                    <td class="dotted-black">&nbsp;</td>
                    <td class="dotted-black" >
                        Language
<select id="lang">
    <option>JavaScript</option>
    <option>Apex</option>
    <option>SOQL</option>
    <option>SOSL</option>
</select>
                    </td>
                    <td class="dotted-black">&nbsp;</td>
                    <td class="dotted-black">
 Multi Line
<input type="checkbox" id="multiline"
       onclick="javascript: if (this.checked)println('Ctrl-Enter to execute script', 'input'); void 0;" 
       name="Multi line" value="Multi line"/>

                    </td>
                    <td width=100% class="dotted-black">&nbsp;</td>
                </tr>
             </table>

            <!--
<input type="button" onclick="javascript:displayElement('help-text'); void 0;" name="Help" value="Help"/>
<input type="button"
       onclick="javascript:document.getElementById('lang').value = 'JavaScript'; go('clear()'); void 0;" 
       name="Clear" value="Clear"/>
       -->

<div id="help-text" style="display: none">

    <p>
    The text box at the bottom of this page is a javascript command line -- you can type
    javascript into it and have it run.   The interpreter is preloaded wth the
    ajax toolkit and the toolkit already has a session by virtue being in an
    scontrol.
    </p>

    <p>
    So simple example is typeing:
    </p>

    <b><pre>
    sforce.connection.query("select name, (select name from contacts) from account");
    </pre></b>

    <p>
    And hitting return.   That should get you back a query result (check out the
    the dhtml).   You can do arbitrary javascript too; and you can do multiline
    commands:
    </p>

    <b><pre>
    var result = sforce.connection.query("select name from account");
    var records = result.records;
    for(var j in records) {
      print(records[j].name)
    }
    </pre></b>

    <p>
    You have to hit shift-return to do multi-line input.
    </p>

    <p>
    The shell also has completion.  So typing 'sforce.conn[TAB]' completes to
    'sforce.connection'.  When there are multiple completions, a second tab will
    get you the list of possibilities.   So typing
    'sforce.connection.[TAB][TAB}' will get you a list of methods on connection
    (ie all the methods on our API).     Handy way to explore.
    </p>

    <p>
    The shell is also useful when opened from an scontrol, using
    sforce.debug.open().   When you do that the shell runs in the context of the
    scontrols DOM.  So you can manipulate the scontrols DOM from the shell.
    A nice way to experiment with code/dom manipulations til you get the effect
    you want.
    </p>

    <p>
    Features: autocompletion of property names with Tab, multiline input with Shift+Enter,
    input history with Up/Down Values and functions: ans, print(string),
    <a accesskey="P" href="javascript:go('props(ans)')" title="Accesskey: P">props(object)</a>,
    <a accesskey="B" href="javascript:go('blink(ans)')" title="Accesskey: B">blink(node)</a>,
    <a accesskey="C" href="javascript:go('clear()')" title="Accesskey: C">clear()</a>,
    load(scriptURL), scope(object)
    </p>
</div>

<div id="output" style="font-family: Courier New, Courier, monospace">
</div>

<div id="input-div">
    <textarea style="font-family: Courier New, Courier, monospace" id="input" class="input" wrap="off" readonly="true"
               onkeydown="inputKeydown(event)" rows="1"></textarea> </div>
</body>

</html>
